<template>
  <el-select v-model="value" ref="selectTree" class="selectTree" filterable @keyup.enter.native.stop="handleSelectTreeFilterRun" :filter-method="handleSelectTreeFilter" @blur="handleSelectTreeBlur" clearable @visible-change="handleSelectVisble" @clear="handleSelectTreeClear" :popper-append-to-body="false" style="width: 100%;" placeholder="请选择选项 或 试试输入关键字后回车搜索">
    <!-- 这个option的:value="value"是为了select的:value="value"去绑定option -->
    <!-- 这个option的:label是为了不展开的时候回显的文字 -->
    <el-option :value="value" :label="filtering ? filterText : label">
      <el-tree ref="tree" :data="treeData" node-key="value" @node-click="handleSelectTreeNodeClick" :filter-node-method="handleSelectTreeFilterNode" :highlight-current="true"></el-tree>
    </el-option>
  </el-select>
</template>

<script>
import treeData from '@/common/apiData/treeData.json'
export default {
  props: {
    // selectTree的tree数据
    data: {
      require: true,
      default: () => {
        return treeData;
      }
    },
    // 选中的值的key（可以支持初始化默认选中）
    selectValue: {
      require: false,
      default: ""
    },
    // 选中的值的label（可以支持初始化默认选中）
    selectLabel: {
      require: false,
      default: ""
    }
  },
  mounted () {
    this.expandTreeNode(this.value);
  },
  data () {
    return {
      // 可以回显
      label: this.selectLabel,
      value: this.selectValue,
      filtering: false,
      filterText: null,
      treeData: this.data,
    }
  },
  methods: {
    /**
     * selectTree选中某一个树节点
     * @param data 属性的数组中该节点所对应的对象
     * @param node 节点对应的
     * @param elem 节点组件本身
     */
    handleSelectTreeNodeClick (data, node, elem) {
      if (!node.isLeaf) {
        // 点击非叶子节点不处理
        return;
      }
      this.label = data.label;
      this.value = data.value;
      this.$refs.selectTree.blur();
    },
    handleSelectTreeClear () {
      this.label = "";
      this.value = "";
    },
    /**
     * select输入关键字会触发该方法进行filter
     * @param val 输入关键字
     */
    handleSelectTreeFilter (val) {
      if (!val) {
        // filterText为空时设置为零宽字符，避免filterText为空显示value（暂时这样解决）
        this.filterText = "\u202c";
      } else {
        this.filterText = val;
      }
      this.filtering = true;
    },
    /**
     * selectTree检索
     */
    handleSelectTreeFilterRun () {
      event.stopPropagation();
      if (this.loading) {
        return // 加载中
      }
      this.loading = true;
      const loading = this.$loading({
        text: "正在搜索中", background: 'rgba(255,255,255,0.5)', target: '.selectTree .el-select-dropdown'
      });
      // 该方法会触发filter-node-method，即 handleSelectTreeFilterNode
      this.$refs.tree.filter(this.filterText);
      this.loading = false;
      loading.close();
    },
    /**
     * 过滤selectTree中节点的逻辑
     * @return true在selectTree中显示该节点；false反之不显示
     */
    handleSelectTreeFilterNode (value, data, node) {
      if (value.startsWith("\u202c")) {
        value = value.substring(1);
      }
      if (!value) {
        return true; // 没有搜索关键字，直接返回
      }
      return data.label.indexOf(value) !== -1;
    },
    handleSelectTreeBlur () {
      this.filterText = "";
      this.filtering = false;
    },
    /**
     * 下拉框出现/隐藏时触发
     * @param visible true出现；false隐藏
     */
    handleSelectVisble (visible) {
      if (!visible) {
        // 隐藏tree时，设置tree，只展开选中的节点
        this.expandTreeNode(this.value);
        // 让tree的所有节点变为显示状态，防止打开后检索了但是不选择，二次打开的时候tree显示不全
        this.$refs.tree.filter("");
      } else {
        // 展开tree时，设置滚动条默认的位置。防止打开select的时候，tree的滚动条有时在顶部，有时候在底部 
        this.$nextTick(() => {
          var ele = document.querySelector('.selectTree .el-scrollbar .el-scrollbar__wrap')
          //默认在顶部
          ele.scrollTop = ele.scrollHeight;
          //默认在底部
          //ele.scrollTop = 0;
        })
      }
    },
    expandTreeNode (nodeId) {
      // 参考：https://blog.csdn.net/weixin_42052678/article/details/125175826
      let treeNodes = this.$refs.tree.store._getAllNodes();
      for (let i = 0, length = treeNodes.length; i < length; i++) {
        treeNodes[i].expanded = false;
      }
      if (!nodeId) {
        return;
      }
      for (let i = 0, length = treeNodes.length; i < length; i++) {
        let treeItem = treeNodes[i];
        if (treeItem.data.value === nodeId) {
          treeItem.expanded = true;
          let treeParentItem = treeItem.parent;
          while (treeParentItem != null && treeParentItem.data.value) {
            treeParentItem.expanded = true;
            treeParentItem = treeParentItem.parent;
          }
        }
      }
    }
  },
  watch: {
    value () {
      // 父子组件双向绑定
      this.$emit("update:selectValue", this.value)
      this.$emit("update:selectLabel", this.label)
    }
  }
}
</script>

<style lang="less" scoped>
@import "./css/selectTree.less";
</style>